﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace ANZ_AIM_FRAMEWORK
{
    /// <summary>
    /// AES encypt/decrypt class
    /// Derived from SimpleAES class by Mark Brittingham
    /// Source: http://stackoverflow.com/questions/165808/simple-2-way-encryption-for-c-sharp
    /// Modification: using AesManaged class instead of RijndaelManaged
    /// </summary>
    public class AESHelper
    {
        // Change these keys
        private byte[] Key = { 65, 78, 90, 66, 97, 110, 107, 105, 110, 103, 71, 114, 111, 117, 112, 76, 
                               105, 109, 105, 116, 101, 100, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42 };
        private byte[] Vector = { 65, 78, 90, 86, 73, 69, 84, 78, 65, 77,
                                  42, 42, 42, 42, 42, 42};

        private ICryptoTransform Encryptor, Decryptor;
        private System.Text.UTF8Encoding UTFEncoder;

        public AESHelper()
        {
            //This is our encryption method
            //RijndaelManaged rm = new RijndaelManaged();
            AesManaged rm = new AesManaged();

            //Create an encryptor and a decryptor using our encryption method, key, and vector.
            Encryptor = rm.CreateEncryptor(this.Key, this.Vector);
            Decryptor = rm.CreateDecryptor(this.Key, this.Vector);

            //Used to translate bytes to text and vice versa
            UTFEncoder = new System.Text.UTF8Encoding();
        }

        // The following 2 methods are used to get the key and vector in plain text
        public string GetKeyStr()
        {
            return UTFEncoder.GetString(this.Key);
        }

        public string GetVectorStr()
        {
            return UTFEncoder.GetString(this.Vector);
        }

        // -------------- Two Utility Methods (not used but may be useful) -----------
        // Generates an encryption key.
        /*static public byte[] GenerateEncryptionKey()
        {
            //Generate a Key.
            RijndaelManaged rm = new RijndaelManaged();
            rm.GenerateKey();
            return rm.Key;
        }

        /// Generates a unique encryption vector
        static public byte[] GenerateEncryptionVector()
        {
            //Generate a Vector
            RijndaelManaged rm = new RijndaelManaged();
            rm.GenerateIV();
            return rm.IV;
        }*/


        // ----------- The commonly used methods ------------------------------    
        // Encrypt some text and return a string suitable for passing in a URL.
        public string EncryptToString(string TextValue)
        {
            return ByteArrToString(Encrypt(TextValue));
        }

        // Encrypt some text and return an encrypted byte array.
        public byte[] Encrypt(string TextValue)
        {
            //Translates our text value into a byte array.
            Byte[] bytes = UTFEncoder.GetBytes(TextValue);

            //Used to stream the data in and out of the CryptoStream.
            using (MemoryStream memoryStream = new MemoryStream())
            {
                /*
                 * We will have to write the unencrypted bytes to the stream,
                 * then read the encrypted result back from the stream.
                 */
                //#region Write the decrypted value to the encryption stream
                using (CryptoStream cs = new CryptoStream(memoryStream, Encryptor, CryptoStreamMode.Write))
                {
                    cs.Write(bytes, 0, bytes.Length);
                    cs.FlushFinalBlock();
                    //#endregion

                    //#region Read encrypted value back out of the stream
                    memoryStream.Position = 0;
                    byte[] encrypted = new byte[memoryStream.Length];
                    memoryStream.Read(encrypted, 0, encrypted.Length);
                    //#endregion

                    //Clean up.
                    //cs.Close();
                    //memoryStream.Close();

                    return encrypted;
                }
            }
        }

        // The other side: Decryption methods
        public string DecryptToString(string EncryptedString)
        {
            return Decrypt(StrToByteArray(EncryptedString));
        }

        /// Decryption when working with byte arrays.    
        public string Decrypt(byte[] EncryptedValue)
        {
            //#region Write the encrypted value to the decryption stream
            using (MemoryStream encryptedStream = new MemoryStream())
            {
                using (CryptoStream decryptStream = new CryptoStream(encryptedStream, Decryptor, CryptoStreamMode.Write))
                {
                    decryptStream.Write(EncryptedValue, 0, EncryptedValue.Length);
                    decryptStream.FlushFinalBlock();
                    //#endregion

                    //#region Read the decrypted value from the stream.
                    encryptedStream.Position = 0;
                    Byte[] decryptedBytes = new Byte[encryptedStream.Length];
                    encryptedStream.Read(decryptedBytes, 0, decryptedBytes.Length);
                    encryptedStream.Close();
                    //#endregion
                    return UTFEncoder.GetString(decryptedBytes);
                }
            }
        }

        // Convert a string to a byte array.  NOTE: Normally we'd create a Byte Array from a string using an ASCII encoding (like so).
        //      System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
        //      return encoding.GetBytes(str);
        // However, this results in character values that cannot be passed in a URL.  So, instead, I just
        // lay out all of the byte values in a long string of numbers (three per - must pad numbers less than 100).
        public byte[] StrToByteArray(string str)
        {
            if (str.Length == 0)
                throw new Exception("Invalid string value in StrToByteArray");

            byte val;
            byte[] byteArr = new byte[str.Length / 3];
            int i = 0;
            int j = 0;
            do
            {
                val = byte.Parse(str.Substring(i, 3));
                byteArr[j++] = val;
                i += 3;
            }
            while (i < str.Length);
            return byteArr;
        }

        // Same comment as above.  Normally the conversion would use an ASCII encoding in the other direction:
        //      System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
        //      return enc.GetString(byteArr);    
        public string ByteArrToString(byte[] byteArr)
        {
            byte val;
            string tempStr = "";
            for (int i = 0; i <= byteArr.GetUpperBound(0); i++)
            {
                val = byteArr[i];
                if (val < (byte)10)
                    tempStr += "00" + val.ToString();
                else if (val < (byte)100)
                    tempStr += "0" + val.ToString();
                else
                    tempStr += val.ToString();
            }
            return tempStr;
        }
    }
}
